package com.sunye.utils;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
import java.util.Set;

import javax.annotation.Resource;

import com.github.tobato.fastdfs.domain.MataData;
import com.github.tobato.fastdfs.service.DefaultGenerateStorageClient;
import com.github.tobato.fastdfs.service.FastFileStorageClient;
import com.sunye.upload.config.OwnThumbImageConfig;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.Validate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.github.tobato.fastdfs.domain.StorageNode;
import com.github.tobato.fastdfs.domain.StorePath;
import com.github.tobato.fastdfs.domain.ThumbImageConfig;
import com.github.tobato.fastdfs.exception.FdfsUnsupportImageTypeException;
import com.github.tobato.fastdfs.exception.FdfsUploadImageException;
import com.github.tobato.fastdfs.proto.storage.StorageSetMetadataCommand;
import com.github.tobato.fastdfs.proto.storage.StorageUploadFileCommand;
import com.github.tobato.fastdfs.proto.storage.StorageUploadSlaveFileCommand;
import com.github.tobato.fastdfs.proto.storage.enums.StorageMetdataSetType;

import net.coobird.thumbnailator.Thumbnails;

/**
 * 面向应用的接口实现
 *
 * @author tobato
 *
 */
@Component
public class DefaultFastFileStorageClientUtil extends DefaultGenerateStorageClient {

    /** 支持的图片类型 */
    private static final String[] SUPPORT_IMAGE_TYPE = { "JPG", "JPEG", "PNG", "GIF", "BMP", "WBMP" };
    private static final List<String> SUPPORT_IMAGE_LIST = Arrays.asList(SUPPORT_IMAGE_TYPE);

    @Autowired
    private OwnThumbImageConfig ownThumbImageConfig;

    /** 缩略图生成配置 */
    @Resource
    private ThumbImageConfig thumbImageConfig;

    /**
     * 上传文件
     */
    public StorePath uploadFile(InputStream inputStream, long fileSize, String fileExtName, Set<MataData> metaDataSet) {
        Validate.notNull(inputStream, "上传文件流不能为空");
        Validate.notBlank(fileExtName, "文件扩展名不能为空");
        StorageNode client = trackerClient.getStoreStorage();
        return uploadFileAndMataData(client, inputStream, fileSize, fileExtName, metaDataSet);
    }

    /**
     * 上传图片并且生成缩略图
     */
    public StorePath uploadImageAndCrtThumbImage(InputStream inputStream, long fileSize, String fileExtName,
                                                 Set<MataData> metaDataSet) {
        Validate.notNull(inputStream, "上传文件流不能为空");
        Validate.notBlank(fileExtName, "文件扩展名不能为空");
        // 检查是否能处理此类图片
        if (!isSupportImage(fileExtName)) {
            throw new FdfsUnsupportImageTypeException("不支持的图片格式" + fileExtName);
        }
        StorageNode client = trackerClient.getStoreStorage();
        byte[] bytes = inputStreamToByte(inputStream);

        // 上传文件和mataData
        StorePath path = uploadFileAndMataData(client, new ByteArrayInputStream(bytes), fileSize, fileExtName,
                metaDataSet);
//***   // 上传缩略图
        uploadThumbImage(client, new ByteArrayInputStream(bytes), path.getPath(), fileExtName);
//***   // 上传缩略图2
        uploadThumbImage2(client, new ByteArrayInputStream(bytes), path.getPath(), fileExtName);
        bytes = null;
        return path;
    }

    /**
     * 获取byte流
     *
     * @param inputStream
     * @return
     */
    private byte[] inputStreamToByte(InputStream inputStream) {
        try {
            return IOUtils.toByteArray(inputStream);
        } catch (IOException e) {
            LOGGER.error("image inputStream to byte error", e);
            throw new FdfsUploadImageException("upload ThumbImage error", e.getCause());
        }
    }

    /**
     * 检查是否有MataData
     *
     * @param metaDataSet
     * @return
     */
    private boolean hasMataData(Set<MataData> metaDataSet) {
        return null != metaDataSet && !metaDataSet.isEmpty();
    }

    /**
     * 是否是支持的图片文件
     *
     * @param fileExtName
     * @return
     */
    private boolean isSupportImage(String fileExtName) {
        return SUPPORT_IMAGE_LIST.contains(fileExtName.toUpperCase());
    }

    /**
     * 上传文件和元数据
     *
     * @param client
     * @param inputStream
     * @param fileSize
     * @param fileExtName
     * @param metaDataSet
     * @return
     */
    private StorePath uploadFileAndMataData(StorageNode client, InputStream inputStream, long fileSize,
                                            String fileExtName, Set<MataData> metaDataSet) {
        // 上传文件
        StorageUploadFileCommand command = new StorageUploadFileCommand(client.getStoreIndex(), inputStream,
                fileExtName, fileSize, false);
        StorePath path = connectionManager.executeFdfsCmd(client.getInetSocketAddress(), command);
        // 上传matadata
        if (hasMataData(metaDataSet)) {
            StorageSetMetadataCommand setMDCommand = new StorageSetMetadataCommand(path.getGroup(), path.getPath(),
                    metaDataSet, StorageMetdataSetType.STORAGE_SET_METADATA_FLAG_OVERWRITE);
            connectionManager.executeFdfsCmd(client.getInetSocketAddress(), setMDCommand);
        }
        return path;
    }

    /**
     * 上传缩略图
     *
     * @param client
     * @param inputStream
     * @param masterFilename
     * @param fileExtName
     */
    private void uploadThumbImage(StorageNode client, InputStream inputStream, String masterFilename,
                                  String fileExtName) {
        ByteArrayInputStream thumbImageStream = null;
        try {
            thumbImageStream = getThumbImageStream(inputStream);// getFileInputStream
            // 获取文件大小
            long fileSize = thumbImageStream.available();
            // 获取缩略图前缀
            String prefixName = ownThumbImageConfig.getPrefixName();//调用自定义宽高
            StorageUploadSlaveFileCommand command = new StorageUploadSlaveFileCommand(thumbImageStream, fileSize,
                    masterFilename, prefixName, fileExtName);
            connectionManager.executeFdfsCmd(client.getInetSocketAddress(), command);

        } catch (IOException e) {
            LOGGER.error("upload ThumbImage error", e);
            throw new FdfsUploadImageException("upload ThumbImage error", e.getCause());
        } finally {
            IOUtils.closeQuietly(thumbImageStream);
        }
    }


    /**
     * 上传缩略图
     *
     * @param client
     * @param inputStream
     * @param masterFilename
     * @param fileExtName
     */
    private void uploadThumbImage2(StorageNode client, InputStream inputStream, String masterFilename,
                                  String fileExtName) {
        ByteArrayInputStream thumbImageStream = null;
        try {
            thumbImageStream = getThumbImageStream2(inputStream);// getFileInputStream
            // 获取文件大小
            long fileSize = thumbImageStream.available();
            // 获取缩略图前缀
            String prefixName = thumbImageConfig.getPrefixName();
            StorageUploadSlaveFileCommand command = new StorageUploadSlaveFileCommand(thumbImageStream, fileSize,
                    masterFilename, prefixName, fileExtName);
            connectionManager.executeFdfsCmd(client.getInetSocketAddress(), command);

        } catch (IOException e) {
            LOGGER.error("upload ThumbImage error", e);
            throw new FdfsUploadImageException("upload ThumbImage error", e.getCause());
        } finally {
            IOUtils.closeQuietly(thumbImageStream);
        }
    }


    /**
     * 获取缩略图
     *
     * @param inputStream
     * @return
     * @throws IOException
     */
    private ByteArrayInputStream getThumbImageStream(InputStream inputStream) throws IOException {
        // 在内存当中生成缩略图
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        //@formatter:off
        Thumbnails
                .of(inputStream)
                .size(ownThumbImageConfig.getWidthTwo(), ownThumbImageConfig.getHeightTwo())
                .toOutputStream(out);
        //@formatter:on
        return new ByteArrayInputStream(out.toByteArray());
    }


    /**
     * 获取缩略图
     *
     * @param inputStream
     * @return
     * @throws IOException
     */
    private ByteArrayInputStream getThumbImageStream2(InputStream inputStream) throws IOException {
        // 在内存当中生成缩略图
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        //@formatter:off
        Thumbnails
                .of(inputStream)
                .size(thumbImageConfig.getWidth(), thumbImageConfig.getHeight())
                .toOutputStream(out);
        //@formatter:on
        return new ByteArrayInputStream(out.toByteArray());
    }

//    //参考http://www.javashuo.com/article/p-bggjcmqj-kr.html
//    一、指定大小进行缩放 spa
//    //size(宽度, 高度)
//
//            /*
//             * 若图片横比200小，高比300小，不变
//             * 若图片横比200小，高比300大，高缩小到300，图片比例不变
//             * 若图片横比200大，高比300小，横缩小到200，图片比例不变
//             * 若图片横比200大，高比300大，图片按比例缩小，横为200或高为300
//             */
//        Thumbnails.of("images/a380_1280x1024.jpg")
//                .size(200, 300)
//                .toFile("c:/a380_200x300.jpg");
//
//        Thumbnails.of("images/a380_1280x1024.jpg")
//                .size(2560, 2048)
//                .toFile("c:/a380_2560x2048.jpg");
//
//
//    2.按照比例进行缩放 code
//        //scale(比例)
//        Thumbnails.of("images/a380_1280x1024.jpg")
//                .scale(0.25f)
//                .toFile("c:/a380_25%.jpg");
//
//        //若是scale设为1.0，则图片长宽不变。
//        Thumbnails.of("images/a380_1280x1024.jpg")
//                .scale(1.0f)
//                .toFile("c:/a380_110%.jpg");
//
//    3.不按照比例，指定大小进行缩放 component
//    //keepAspectRatio(false) 默认是按照比例缩放的
//    Thumbnails.of("images/a380_1280x1024.jpg")
//        .size(200, 200)
//        .keepAspectRatio(false)
//        .toFile("c:/a380_200x200.jpg");
//    4.旋转orm
//    //rotate(角度),正数：顺时针 负数：逆时针
//    Thumbnails.of("images/a380_1280x1024.jpg")
//            .size(1280, 1024)
//            .rotate(90)
//            .toFile("c:/a380_rotate+90.jpg");
//
//    Thumbnails.of("images/a380_1280x1024.jpg")
//            .size(1280, 1024)
//            .rotate(-90)
//            .toFile("c:/a380_rotate-90.jpg");
//    5.水印
//    //watermark(位置，水印图，透明度)
//    Thumbnails.of("images/a380_1280x1024.jpg")
//            .size(1280, 1024)
//            .watermark(Positions.BOTTOM_RIGHT, ImageIO.read(new File("images/watermark.png")), 0.5f)
//                .outputQuality(0.8f)
//            .toFile("c:/a380_watermark_bottom_right.jpg");
//
//    Thumbnails.of("images/a380_1280x1024.jpg")
//            .size(1280, 1024)
//            .watermark(Positions.CENTER, ImageIO.read(new File("images/watermark.png")), 0.5f)
//                .outputQuality(0.8f)
//            .toFile("c:/a380_watermark_center.jpg");
}
